const Controller = require('egg').Controller;

const nowTime = Date.now();
const Web3 = require('web3');
const HDWalletProvider = require('truffle-hdwallet-provider');
const Decimal = require('decimal.js');
const BigNumber = require('bignumber.js');
// var web3 = new Web3();
// web3.setProvider(new Web3.providers.HttpProvider("https://rinkeby.infura.io/v3/ef8cd40931114dabb4032fc0314a4d55"));
const mnemonic = "winner weird snack cry today penalty isolate joy secret turn jealous wagon"
const url = "https://rinkeby.infura.io/v3/ef8cd40931114dabb4032fc0314a4d55";
const web3 = new Web3(new HDWalletProvider(mnemonic, url));

const timeout = 5;
function toInt(str) {
    if (typeof str === 'number') return str;
    if (!str) return str;
    return parseInt(str, 10) || 0;
}
// 创建订单
const createRule = {
    uuid: { type: 'string' },
    actionSelector: { type: 'string' },
    userTimeStamp: { type: 'string' },
    amount: { type: 'string' },
    sign: { type: 'string' },
    actionAddress: { type: 'string' },
    userAddress: { type: 'string' },
    isBuy: { type: 'string' }
};
// 提现
const cashRule = {
    uuid: { type: 'string' },
    actionSelector: { type: 'string' },
    // parameter:  { type: 'string' },
    userTimeStamp: { type: 'string' },
    // srvTimestamp: { type: 'string' },
    amount: { type: 'string' },
    sign: { type: 'string' },
    // actionAddress: { type: 'string' },
    // accountId: { type: 'string' },
    // blockNum: { type: 'string' },
    cash: { type: 'string' }
};
const listRule = {
    userAddress: {type: 'string'}
};

// 准备 定时发送用的
const sendRule = {
    ifFail: {type: 'string'}
};
// 合约rest
const contractRule = {
    accountAddr: {type: 'string'},
    actionCtrAddr: {type: 'string'},
    parameter: {type: 'string'},
    startTime: {type: 'string'},
    endTime: {type: 'string'},
};
// 前段买涨买跌统计
const isBuyCountRule = {
    openTime: {type: 'string'},
    closeTime: {type: 'string'},
}

// ifFail 1 赢 |  0 没有结算 |  2 输  | 3 平
class ActionController extends Controller {
    /**
     * 前端列表显示
     * 参数：limit offset userAddress 
     */
    async index() {
        // const ctx = this.ctx;
        const { ctx, service } = this;
        ctx.status = 200;
        ctx.body = await service.actions.index();
    }
    /**
     * 冻结余额 = 下单冻结 + 提现冻结
     * 参数 userAddress
     */ 
    async blockedBalance() {
        // const ctx = this.ctx;
        const { ctx, service } = this;
        ctx.status = 200;
        let res = await service.actions.blockedBalance();
        if(res.status_code == '422') {
            ctx.status = 422; 
        }else {
            ctx.status = 200; 
        }
        ctx.body = res;
        return;
    }
    /** 
     * 当前买涨/买跌的数量(前端)
     * 涨1 | 跌0
     * */ 
    async isBuyCount() {
        const { ctx, service } = this;
        ctx.status = 200;
        ctx.body = await service.actions.isBuyCount();
        return;
    }

    /**
     * 下订单和提现 在一起
     * 1、验证提交的数据是否为空
     * 2、验证用户是否存在 // 下面验证了签名这一步就不用了
     * 3、验证随机数是否重复
     * 4、下单是否超时
     * 5、从accounts 表根据userAddress拿到account id 赋值给 actions 表的accountId
     * 5、签名是否是这个用户
     * 6、用户合约余额 >= 本地balance - freezeBalance
     * 6、accounts余额什么的都够，生成订单的时候报错用了事务
     * 7、看看这一轮是否同时下了买涨|买跌，下的涨也下了跌那就是isMore 就是true(1)
     * ------------------ 有这个符号的都是需要去合约拿值的 ------------------
     */ 
    async create() {

        const { ctx, service } = this;
       
        let serviceCreate = await service.create.create();
      
        if(serviceCreate.status_code == 422) {
            ctx.status = 422
            ctx.body = serviceCreate
            return
        }
      
        let amount = web3.utils.fromWei(serviceCreate.amount, 'ether');
        let userAddress = ctx.request.body.userAddress
        ctx.request.body.amount =  amount;
        ctx.request.body.uuid = serviceCreate.uuid;
        ctx.request.body.sign = serviceCreate.sign;
        ctx.request.body.isBuy = serviceCreate.isBuy;
        // ctx.request.body.amount = web3.utils.fromWei(amount, 'ether');
        //serviceCreate.amount;
        ctx.request.body.accountId = serviceCreate.accountId;
        ctx.request.body.srvTimestamp = serviceCreate.srvTimestamp;
        ctx.request.body.userTimeStamp = serviceCreate.userTimeStamp;
        ctx.request.body.actionSelector = serviceCreate.actionSelector;
        ctx.request.body.actionAddress = ctx.request.body.actionAddress;
        ctx.request.body.encodeParameters = serviceCreate.encodeParameters;

        const conn = await this.app.mysql.beginTransaction();
    
        // 下单的金额够不够用 不够直接catch，够了then的时候形成订单失败也会回滚 
        try {
            let updateRes = await conn.query(`UPDATE accounts SET freezeBalance=freezeBalance+${amount} WHERE userAddress='${userAddress}' and balance-freezeBalance>='${amount}'`)
            // await ctx.model.Action.create
            if(updateRes.affectedRows == 1) {
                const { uuid, actionSelector,userTimeStamp, srvTimestamp, amount, sign, actionAddress, accountId, isBuy, encodeParameters } = ctx.request.body;
                
                // 这里有错 没有用事务
                const action = await ctx.model.Action.create({ uuid, actionSelector,userTimeStamp, srvTimestamp, amount, sign, actionAddress, accountId, isBuy, encodeParameters });
                await conn.commit(); // 提交事务
                ctx.status = 201;
                return;
            }else {
                await conn.rollback(); // 一定记得捕获异常后回滚事务！！
                ctx.status = 422;
                ctx.body = {'status_code':422, 'msg': err};
            }
        } catch(err) {
            await conn.rollback(); // 一定记得捕获异常后回滚事务！！
            ctx.status = 422;
            ctx.body = {'status_code':422, 'msg': err};
        }
       
    }

    // 下面是和合约有关系的
    async cashCreate() {
        const { ctx, service } = this;
       
        // 提现 await service.actions.
        let serviceCreate = await service.create.cashCreate();
       
        if(serviceCreate.status_code == 422) {
            ctx.status = 422
            ctx.body = serviceCreate
            return
        }
      
        let amount = web3.utils.fromWei(serviceCreate.amount, 'ether');
        
        let userAddress = ctx.request.body.userAddress
        
        let sign = serviceCreate.sign;
        let isBuy = 3;
        let uuid = serviceCreate.uuid;
        let accountId = serviceCreate.accountId;
        let srvTimestamp = serviceCreate.srvTimestamp;
        let userTimeStamp = serviceCreate.userTimeStamp;
        let actionSelector = serviceCreate.actionSelector;
        let actionAddress = ctx.request.body.actionAddress;
        let encodeParameters = serviceCreate.encodeParameters;
       
        const conn = await this.app.mysql.beginTransaction();

        try {
            let updateRes = await conn.query(`UPDATE accounts SET freezeBalance=freezeBalance+${amount} WHERE userAddress='${userAddress}' and balance-freezeBalance>=${amount}`)
            
            if(updateRes.affectedRows == 1) {
                const action = await conn.query(
                    'INSERT INTO actions (uuid, actionSelector,userTimeStamp, srvTimestamp, amount, sign, actionAddress, accountId, isBuy, encodeParameters) VALUES (?,?,?,?,?,?,?,?,?,?)',[uuid,actionSelector,userTimeStamp,srvTimestamp,amount,sign,actionAddress,accountId,isBuy,encodeParameters]);
              
                // 合约的 WithDraw_CtrAddr
                
                let contract = new web3.eth.Contract(await service.abi.WithDraw_ABI(), await service.abi.WithDraw_CtrAddr());
                let accounts = await web3.eth.getAccounts();
                let result = null;
                contract.methods.ActionCallBack_WithDraw(uuid, serviceCreate.amount, sign).send({
                    from: accounts[0],
                }).on('confirmation', function(confirmationNumber, receipt){
                    if(confirmationNumber > 0) {
                        result = receipt
                    }
                })
               
            }else {
                await conn.rollback(); // 一定记得捕获异常后回滚事务！！
                ctx.status = 422;
                ctx.body = {'status_code':422, 'msg': '进try报错'};
                return; 
            }
            //这里也是要 conn.commit();
            await conn.commit();
        } catch(err) {
            await conn.rollback(); // 一定记得捕获异常后回滚事务！！
            ctx.status = 422;
            ctx.body = {'status_code':422, 'msg': '直接没进try'};
            return;
        }
        ctx.status = 200;
        ctx.body = {'status_code':200, 'msg': '提现成功！'};
       
    }
    // ifFail 成功1 | 失败0 失败的要再次发送 ，isSettled 是否已结算 未结算0 | 已结算1
    async sendContract() {
        // const ctx = this.ctx;
        const { ctx, service } = this;
        //
        // ifFail 就是发送失败或者没有发送的
        const query = {
            where: {
                srvTimestamp: {
                    $between: [toInt(ctx.query.startTime),toInt(ctx.query.endTime)]
                }
            }     
        };
        console.log([toInt(ctx.query.startTime),toInt(ctx.query.endTime)]);
        const actions = await ctx.model.Action.findAll(query);
        
        // 数据库没有一条符合条件的轮询，直接结束
        if (actions.length == 0) {
            ctx.status = 422;
            ctx.body = {'status_code':422, 'msg': '没有单子要传！'};
            return;
        }
        // 
        // let web3 = new Web3(new HDWalletProvider(mnemonic, url));
        // ctx.status = 201;
        // ctx.body = actions;
        // return;
        let contract = new web3.eth.Contract(await service.abi.Order_ABI(), await service.abi.Order_CtrAddr());
        let accounts = await web3.eth.getAccounts();
        // console.log(contract2.methods)
        for (var i=0;i<actions.length;i++)
        {
            
            const accountRow = await ctx.model.Account.findOne({
                where: {
                    id: actions[i].accountId
                }
            });
                 
            let uuid = actions[i].uuid;
            // let userAddr = accountRow.userAddress;
            var tradeAmount = web3.utils.toWei(actions[i].amount, 'ether');
            let isBuy = actions[i].isBuy == 1 ? true : false;
            let srvTimestamp = actions[i].srvTimestamp;
            let sign = actions[i].sign; // uuid,userAddr,tradeAmount,isBuy,srvTimestamp,sign
            console.log({uuid,tradeAmount,isBuy,srvTimestamp,sign});
            // let result = await contract.methods.ActionCallBack_SettleOrder(uuid, tradeAmount, isBuy, srvTimestamp, sign).send({
            //     from: accounts[0],
            // })
            let result = null;
            let nonce = await web3.eth.getTransactionCount(accounts[0], 'pending');
            await contract.methods.ActionCallBack_SettleOrder(uuid, tradeAmount,isBuy, srvTimestamp, sign).send({
                from: accounts[0],
                gas: 4900000 + i
            }).on('confirmation', function(confirmationNumber, receipt){
                if(confirmationNumber > 0) {
                    result = receipt
                }
            }).on('error', function(error) {
                
                console.log('error',error)
            })
            ctx.status = 201;
            ctx.body = { 
                'uuid': uuid,
                'tradeAmount': tradeAmount,
                'isBuy': isBuy,
                'srvTimestamp': srvTimestamp,
                'sign': sign,
            };
            console.log(result);

        }
      
        
    }
    // 此单平局！
    async updateActionOrBalance() {
        const ctx = this.ctx;
        let uuid = ctx.query.uuidDogFall;
        let userAddress = ctx.query.userAddrDogFall;
        let balance = ctx.query.tradeAmountDogFall;
        let event = ctx.query.event;
        balance =  web3.utils.fromWei(ctx.query.balance.toString(), 'ether');//amount;
        let accountAddress = "0x77b6901a7e9491d3cb8bfb9d82237f3f1d0542d2"

        const conn = await this.app.mysql.beginTransaction();
        // 下单的金额够不够用 不够直接catch，够了then的时候形成订单失败也会回滚 
        // 判断是加还是数据库是入金 分奖池 还是提现 
        if(event == "DogFall") { 
            await conn.query(`UPDATE accounts SET balance=balance+${balance} WHERE userAddress='${userAddress}'`).then( async result => {
                // 修改状态
                const ActionCastRes = await ctx.model.Action.findOne({
                    where: {
                        uuid: uuid
                    }
                });
                // 合约返回的时候还有一个特殊的值 用于判断是否成功 赢 1 输2 平局 3
                await ActionCastRes.update({ ifFail:3, allocation: balance }); // ifFail 1 赢 |  0 没有结算 |  2 输
                await conn.commit(); // 提交事务
            })
        }
        ctx.status = 201;
        ctx.body = { 'status_code': 201, 'data': '此单平局！' };
    }
    /**
     * 智能合约rest
     * 先判断 accounts 是否有这个 accountAddr 有形成一个数组，然后判断actions 是否在这个数组中
     *  */
  
    async contractRes() {
        const ctx = this.ctx;
        
        // ctx.validate(contractRule, ctx.query);
        let actionRes = await ctx.model.Account.findAll({
            where: {
                accountAddress: ctx.query.accountAddr
            }
        });
       
        // 符合 accountAddr 存一个数组
        let accountIdArr = [];
        actionRes.forEach(async function (row) {
            accountIdArr.push(row.id);
        });
        
        // 把发送失败的要再次去发送的
        let ifFail = 0;
        // 
        const query = {
            where: {
                // actionAddress: ctx.query.actionCtrAddr,
                // parameter: ctx.query.parameter,
                // ifFail: ifFail,  
                isBuy: ctx.query.isBuy,
                srvTimestamp: {
                    $between: [toInt(ctx.query.startTime),toInt(ctx.query.endTime)]
                }
            },
        };

        const actions = await ctx.model.Action.findAll(query);
        let addAmount = 0;
        actions.forEach(async function (row) {
            addAmount = new BigNumber(addAmount).plus(new BigNumber(row.amount));
        });

        ctx.status = 200;
        ctx.body = { 'status_code': 200, 'currBalance': web3.utils.toWei(addAmount.toString(), 'ether') };
        return;
    }
    // 监听用的 入金
    async accountBalance() {
        const ctx = this.ctx;
        let userAddress = ctx.query.userAddrDeposit;
        let balance = ctx.query.amountDeposit;
        let event = ctx.query.event;
        balance =  web3.utils.fromWei(balance, 'ether');//amount;
        let accountAddress = "0x77b6901a7e9491d3cb8bfb9d82237f3f1d0542d2"
        // 没有就创建，有就更新balance
        await ctx.model.Account.findOrCreate({
            where: {userAddress: userAddress}, 
            defaults: {accountAddress: accountAddress, userAddress: userAddress, balance: balance, freezeBalance: 0}
          })
          .spread(async (action, created) => {
            if(created === false) {
                const conn = await this.app.mysql.beginTransaction();
                // 下单的金额够不够用 不够直接catch，够了then的时候形成订单失败也会回滚 
                // 判断是加还是数据库是入金 分奖池 还是提现 
                if(event == "Withdraw") { 
                    await conn.query(`UPDATE accounts SET freezeBalance=freezeBalance+${balance} WHERE userAddress='${userAddress}'`).then( async result => {
                        await conn.commit(); // 提交事务
                        // 这里没用
                        ctx.status = 201;
                        ctx.body = {'status_code':201, 'msg': "提现 成功！"};
                    })
                }else {
                    await conn.query(`UPDATE accounts SET balance=balance+${balance} WHERE userAddress='${userAddress}'`).then( async result => {
                        await conn.commit(); // 提交事务
                        ctx.status = 201;
                        ctx.body = {'status_code':201, 'msg': "balance 成功！"};
                    })
                }
            }
          })
        ctx.status = 201;
        ctx.body = { 'status_code': 201, 'data': '入金成功！' };
    }

    // 监听修改 ifFail 输|赢|平局
    async updateAction() {
        const ctx = this.ctx;
        // let uuid = uuid;
        let constractUuid = ctx.query.uuid; // 这个应该是合约返回的值
        let allocation = ctx.query.allocation;
        let amount = ctx.query.amount;
        const ActionCastRes = await ctx.model.Action.findOne({
            where: {
                uuid: constractUuid
            }
        });
        // ctx.body = { 'status_code': 201, 'data': allocation };
        // 合约返回的时候还有一个特殊的值 用于判断是否成功 成功1 失败是2
        if(allocation == 'true') {
            await ActionCastRes.update({ ifFail:1, allocation: web3.utils.fromWei(amount, 'ether') }); // ifFail 1 赢 |  0 没有结算 |  2 输
        }else {
            await ActionCastRes.update({ ifFail:2 });
        }
        ctx.status = 201;
        ctx.body = { 'status_code': 201, 'data': '订单修改成功！' };
    }
    
    // 监听 异常单(合约没有数据或者平局) - 退钱
   
    
}

module.exports = ActionController;